package com.nuo.common.quartz.core.operate;

import com.nuo.common.quartz.core.enums.JobStateEnum;
import com.nuo.common.quartz.core.entity.QuartzJobDO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 任务操作基础封装
 *
 * @author LGC
 */
@Slf4j
public class QuartzOperate {

    private static final String SCHEDULE_NAME = "NUO_JOB_";

    @Resource
    private Scheduler scheduler;

    /**
     * 触发器 KEY
     */
    public TriggerKey getTriggerKey(Integer jobId) {
        return TriggerKey.triggerKey(SCHEDULE_NAME + jobId);
    }

    /**
     * 定时器 Key
     */
    public JobKey getJobKey(Integer jobId) {
        return JobKey.jobKey(SCHEDULE_NAME + jobId);
    }

    /**
     * 表达式触发器
     */
    public CronTrigger getCronTrigger(Integer jobId) {
        try {
            return (CronTrigger) this.scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("获取定时任务CronTrigger出现异常", e);
        }
    }

    /**
     * 创建定时器
     */
    public void createJob(QuartzJobDO quartzJobDO) {
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(QuartzRecord.class).withIdentity(getJobKey(quartzJobDO.getId())).build();
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                    .cronSchedule(quartzJobDO.getCron())
                    .withMisfireHandlingInstructionDoNothing();
            // 表达式触发器
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(getTriggerKey(quartzJobDO.getId()))
                    .withSchedule(scheduleBuilder).build();
            // 放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(QuartzJobDO.JOB_PARAM_KEY, quartzJobDO);
            try {
                scheduler.scheduleJob(jobDetail, trigger);
            } catch (org.quartz.ObjectAlreadyExistsException existsException) {
                log.info("任务已存在，重新恢复任务");
                this.resumeJob(quartzJobDO.getId());
            }
            // 状态校验
            checkStop(quartzJobDO);
        } catch (SchedulerException e) {
            throw new RuntimeException("创建定时任务失败", e);
        }
    }

    /**
     * 更新定时任务
     */
    public void updateJob(QuartzJobDO quartzJobDO) {
        try {
            TriggerKey triggerKey = getTriggerKey(quartzJobDO.getId());
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                    .cronSchedule(quartzJobDO.getCron())
                    .withMisfireHandlingInstructionDoNothing();
            // 表达式触发器
            CronTrigger trigger = getCronTrigger(quartzJobDO.getId())
                    .getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder).build();
            trigger.getJobDataMap().put(QuartzJobDO.JOB_PARAM_KEY, quartzJobDO);
            scheduler.rescheduleJob(triggerKey, trigger);
            // 状态校验
            checkStop(quartzJobDO);
        } catch (SchedulerException e) {
            throw new RuntimeException("更新定时任务失败", e);
        }
    }

    /**
     * 暂停任务
     */
    public void pauseJob(Integer jobId) {
        try {
            this.scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("暂停定时任务失败", e);
        }
    }

    /**
     * 恢复定时器
     */
    public void resumeJob(Integer jobId) {
        try {
            this.scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("恢复定时任务失败", e);
        }
    }

    /**
     * 删除定时器
     */
    public void deleteJob(Integer jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("删除定时器失败", e);
        }
    }

    /**
     * 执行定时器
     */
    public void run(QuartzJobDO quartzJobDO) {
        try {
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzJobDO.JOB_PARAM_KEY, quartzJobDO);
            this.scheduler.triggerJob(getJobKey(quartzJobDO.getId()), dataMap);
        } catch (SchedulerException e) {
            throw new RuntimeException("立即执行定时任务失败", e);
        }
    }

    /**
     * 校验停止定时器
     */
    public void checkStop(QuartzJobDO quartzJobDO) {
        if (!quartzJobDO.getState().equals(JobStateEnum.JOB_RUN.getStatus())) {
            this.pauseJob(quartzJobDO.getId());
        }

    }

}
